// (C) Copyright 2012 Kystar. All rights reserved.

`timescale 1ns/100ps
`default_nettype none

module net_disp_data_pkg 
(
    input  wire          I_sclk,
    input  wire          I_rst_n,
    //
    input  wire          I_new_frame,
    input  wire          I_video_not_active,
    //
    output reg           O_req_net,
    input  wire          I_ack_net,
    // output
    output reg           O_net_out_en,
    output reg  [ 7: 0]  O_net_out_data,
    // fifo
    input  wire [ 15: 0] I_fifo_usedw,
    output reg           O_fifo_rdreq,
    input  wire [ 23: 0] I_fifo_rddata,
    // ram interface - 64 Byte
    output reg  [ 5: 0]  O_disp_data_ram_rdaddr,
    input  wire [ 7: 0]  I_disp_data_ram_rddata,
    // for idle pkg
    input  wire          I_disable_idle_pkg,
    output reg           O_send_idle_pkg_start,
    input  wire          I_send_idle_pkg_ok,
    // registers
    input  wire          I_reg_px_enable,
    input  wire [ 10: 0] I_reg_px_start_row,
    input  wire [ 11: 0] I_reg_px_start_col,
    input  wire [ 11: 0] I_reg_px_width,
    input  wire [ 10: 0] I_reg_px_height,
    input  wire          I_reg_mac_addr_incr_en,
    input  wire [ 11: 0] I_reg_max_pixel_num_in_one_trans,
    input  wire          I_reg_long_pkg_en,
    input  wire [ 11: 0] I_reg_px_line_step

);

/******************************************************************************
                                <localparams>
******************************************************************************/
localparam // pkg_state
    PKG_IDLE = 0,
    PKG_WAIT_FIFO = 1,
    PKG_REQ_NET = 1<<1,
    PKG_SEND_NET_HEAD = 1<<2,
    PKG_SEND_UDP = 1<<3,
    PKG_SEND_VIDEO_DATA = 1<<4,
    PKG_END = 1<<5;

localparam // state
    IDLE = 0,
    SEND_LINE_PRE = 1,
    SEND_LINE_START = 1<<1,
    SEND_LINE = 1<<2,
    SEND_LINE_POST = 1<<3,
    SEND_LINE_POST_UPDATE_ROW = 1<<4,
    IS_ALL_LINE_SENT = 1<<5,
    SEND_IDLE_PKG_PRE = 1<<6,
    SEND_IDLE_PKG = 1<<7,
    SEND_IDLE_PKG_POST = 1<<8,
    WAIT_FOR_A_WHILE = 1<<9;

localparam
    NET_HEAD_BYTES = 8,
    NET_UDP_BYTES  = 63 - 8;

localparam // line_state
    LINE_IDLE = 0,
    LINE_INIT = 1,
    PKG_PRE = 1<<1,
    PKG_START = 1<<2,
    PKG_SENDING = 1<<3,
    PKG_POST = 1<<4,
    IS_LINE_SENT = 1<<5;


/******************************************************************************
                              <internal signals>
******************************************************************************/
reg  [ 10: 0] line_cnt;
wire send_line_start;
reg  [ 9: 0] state;
reg  [ 9: 0] next_state;
reg  send_line_ok;
reg  [ 11: 0] left_pixel;
reg  [ 11: 0] cur_pkg_pixel_num;
reg  [ 13: 0] cur_pkg_pixel_num_x_3_sub_1;
wire send_pkg_start;
reg  [ 10: 0] start_row;
reg  [ 11: 0] start_col;
reg  [ 10: 0] row;
reg  [ 11: 0] col;
reg  [ 5: 0] line_state;
reg  [ 5: 0] next_line_state;
reg  [ 5: 0] pkg_state;
reg  [ 5: 0] next_pkg_state;
reg  pkg_send_ok;
reg  [ 2: 0] head_cnt;
reg  [ 7: 0] udp_cnt;
reg  [ 11: 0] fifo_rdreq_cnt;
reg  fifo_rdreq_dly;
reg  [ 1: 0] is_rgb_cnt;
wire is_r;
wire is_g;
wire is_b;
reg  [ 12: 0] video_data_byte_cnt;
reg  [ 7: 0] wait_cnt;
reg  [ 7: 0] idle_pkg_cnt;
wire [ 11: 0] reg_px_line_step;
reg  [ 11: 0] next_round_row;
reg  add_next_round_row;
reg  [ 7: 0] disp_data_ram_rddata_dly;
reg  [ 7: 0] row_and_col;
reg  is_row_and_col;

/******************************************************************************
                                <module body>
******************************************************************************/
assign reg_px_line_step = I_reg_px_line_step == 'd0 ? 'd1 : I_reg_px_line_step;

//--------------------------------------------------------------------
// state machine : state
//--------------------------------------------------------------------
always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        state <= IDLE;
    else if (I_new_frame && I_reg_px_enable)
        state <= SEND_LINE_PRE;
    else
        state <= next_state;

always @(*)
    case (state)
        IDLE:
            if (I_video_not_active)
                next_state = SEND_IDLE_PKG_PRE;
            else
                next_state = IDLE;
        SEND_LINE_PRE:
            next_state = SEND_LINE_START;
        SEND_LINE_START:
            next_state = SEND_LINE;
        SEND_LINE:
            if (send_line_ok)
                next_state = SEND_LINE_POST;
            else
                next_state = SEND_LINE;
        SEND_LINE_POST:
            next_state = SEND_LINE_POST_UPDATE_ROW;
        SEND_LINE_POST_UPDATE_ROW:
            next_state = IS_ALL_LINE_SENT;
        IS_ALL_LINE_SENT:
            if (I_disable_idle_pkg || line_cnt == I_reg_px_height)
                next_state = SEND_IDLE_PKG_PRE;
            else
                next_state = SEND_LINE_PRE;
        SEND_IDLE_PKG_PRE:
            next_state = SEND_IDLE_PKG;
        SEND_IDLE_PKG:
            if (I_send_idle_pkg_ok)
                next_state = SEND_IDLE_PKG_POST;
            else
                next_state = SEND_IDLE_PKG;
        SEND_IDLE_PKG_POST:
            if (I_disable_idle_pkg)
                next_state = IDLE;
            else if (I_reg_mac_addr_incr_en && !I_video_not_active && idle_pkg_cnt >= 'd50)
                next_state = IDLE;
            else
                next_state = WAIT_FOR_A_WHILE;
        WAIT_FOR_A_WHILE:
            if (I_disable_idle_pkg)
                next_state = IDLE;
            else if (wait_cnt == 8'hff)
                next_state = SEND_IDLE_PKG_PRE;
            else
                next_state = WAIT_FOR_A_WHILE;
        default:
            next_state = IDLE;
    endcase

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_send_idle_pkg_start <= 1'b0;
    else
        O_send_idle_pkg_start <= state == SEND_IDLE_PKG_PRE;

assign send_line_start = (state == SEND_LINE_START);

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        line_cnt <= 'd0;
    else if (I_new_frame)
        line_cnt <= 'd0;
    else if (state == SEND_LINE_POST)
        line_cnt <= line_cnt + 1;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        wait_cnt <= 'd0;
    else if (state != WAIT_FOR_A_WHILE)
        wait_cnt <= 'd0;
    else
        wait_cnt <= wait_cnt + 1;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        idle_pkg_cnt <= 'd0;
    else if (I_new_frame)
        idle_pkg_cnt <= 'd0;
    else if (state == SEND_IDLE_PKG_PRE)
        idle_pkg_cnt <= idle_pkg_cnt + 1'b1;

//--------------------------------------------------------------------
// state machine : line_state
//--------------------------------------------------------------------
always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        line_state <= LINE_IDLE;
    else if (send_line_start)
        line_state <= LINE_INIT;
    else
        line_state <= next_line_state;

always @(*)
    case (line_state)
        LINE_IDLE:
            next_line_state = LINE_IDLE;
        LINE_INIT:
            next_line_state = PKG_PRE;
        PKG_PRE:
            next_line_state = PKG_START;
        PKG_START:
            next_line_state = PKG_SENDING;
        PKG_SENDING:
            if (pkg_send_ok)
                next_line_state = PKG_POST;
            else
                next_line_state = PKG_SENDING;
        PKG_POST:
            next_line_state = IS_LINE_SENT;
        IS_LINE_SENT:
            if (I_disable_idle_pkg || left_pixel == 'd0)
                next_line_state = LINE_IDLE;
            else
                next_line_state = PKG_PRE;
        default:
            next_line_state = LINE_IDLE;
    endcase

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        send_line_ok <= 1'b0;
    else
        send_line_ok <= line_state == IS_LINE_SENT && (I_disable_idle_pkg || left_pixel == 'd0);

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        left_pixel <= 'd0;
    else if (send_line_start)
        left_pixel <= I_reg_px_width;
    else if (line_state == PKG_POST)
        left_pixel <= left_pixel - cur_pkg_pixel_num;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        cur_pkg_pixel_num <= 'd0;
    else if (line_state == PKG_PRE)
        begin
        if (left_pixel <= (I_reg_long_pkg_en ? I_reg_px_width : I_reg_max_pixel_num_in_one_trans))
            cur_pkg_pixel_num <= left_pixel;
        else if (left_pixel <= (I_reg_long_pkg_en ? {I_reg_px_width,1'b0} : {I_reg_max_pixel_num_in_one_trans,1'b0}))
            cur_pkg_pixel_num <= left_pixel[11:1];
        else
            cur_pkg_pixel_num <= (I_reg_long_pkg_en ? I_reg_px_width : I_reg_max_pixel_num_in_one_trans);
        end

assign send_pkg_start = line_state == PKG_START;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        start_row <= 'd0;
    else if (I_new_frame)
        start_row <= I_reg_px_start_row;
    else if (state == SEND_LINE_POST)
        start_row <= start_row + 1;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        start_col <= 'd0;
    else if (send_line_start)
        start_col <= I_reg_px_start_col;
    else if (line_state == PKG_POST)
        start_col <= start_col + cur_pkg_pixel_num;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        row <= 'd0;
    else if (I_new_frame)
        row <= 'd0;
    else if (state == SEND_LINE_POST)
        row <= row + reg_px_line_step;
    else if (state == SEND_LINE_POST_UPDATE_ROW)
        begin
        if (row >= I_reg_px_height)
            row <= next_round_row;
        end

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        add_next_round_row <= 1'b0;
    else
        add_next_round_row <= (state == SEND_LINE_POST_UPDATE_ROW) && (row >= I_reg_px_height);

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        next_round_row <= 'd0;
    else if (state == IDLE
        || state == SEND_IDLE_PKG_PRE)
        next_round_row <= 'd1;
    else if (add_next_round_row)
        next_round_row <= next_round_row + 1'b1;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        col <= 'd0;
    else if (send_line_start)
        col <= 'd0;
    else if (line_state == PKG_POST)
        col <= col + cur_pkg_pixel_num;

//--------------------------------------------------------------------
// state machine : pkg_state
//--------------------------------------------------------------------
always @(posedge I_sclk)
    cur_pkg_pixel_num_x_3_sub_1 <= cur_pkg_pixel_num + {cur_pkg_pixel_num,1'b0} - 1;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        pkg_state <= PKG_IDLE;
    else if (send_pkg_start)
        pkg_state <= PKG_WAIT_FIFO;
    else
        pkg_state <= next_pkg_state;

always @(*)
    case (pkg_state)
        PKG_IDLE:
            next_pkg_state = PKG_IDLE;
        PKG_WAIT_FIFO:
            if (I_fifo_usedw >= cur_pkg_pixel_num)
                next_pkg_state = PKG_REQ_NET;
            else
                next_pkg_state = PKG_WAIT_FIFO;
        PKG_REQ_NET:
            if (I_ack_net)
                next_pkg_state = PKG_SEND_NET_HEAD;
            else
                next_pkg_state = PKG_REQ_NET;
        PKG_SEND_NET_HEAD:
            if (head_cnt == NET_HEAD_BYTES - 1)
                next_pkg_state = PKG_SEND_UDP;
            else
                next_pkg_state = PKG_SEND_NET_HEAD;
        PKG_SEND_UDP:
            if (udp_cnt == NET_UDP_BYTES - 1)
                next_pkg_state = PKG_SEND_VIDEO_DATA;
            else
                next_pkg_state = PKG_SEND_UDP;
        PKG_SEND_VIDEO_DATA:
            if (video_data_byte_cnt == cur_pkg_pixel_num_x_3_sub_1)
                next_pkg_state = PKG_END;
            else
                next_pkg_state = PKG_SEND_VIDEO_DATA;
        PKG_END:
            next_pkg_state = PKG_IDLE;
        default:
            next_pkg_state = PKG_IDLE;
    endcase

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_req_net <= 1'b0;
    else if (pkg_state == PKG_REQ_NET)
        O_req_net <= 1'b1;
    else if (pkg_state == PKG_END)
        O_req_net <= 1'b0;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_disp_data_ram_rdaddr <= 'd0;
    else if (pkg_state == PKG_SEND_NET_HEAD && head_cnt == NET_HEAD_BYTES - 3)
        O_disp_data_ram_rdaddr <= 'd1;
    else if (pkg_state == PKG_SEND_NET_HEAD && head_cnt == NET_HEAD_BYTES - 2)
        O_disp_data_ram_rdaddr <= 'd2;
    else if (pkg_state == PKG_SEND_NET_HEAD && head_cnt == NET_HEAD_BYTES - 1)
        O_disp_data_ram_rdaddr <= 'd3;
    else if (pkg_state == PKG_SEND_UDP)
        O_disp_data_ram_rdaddr <= O_disp_data_ram_rdaddr + 1;
    else
        O_disp_data_ram_rdaddr <= 'd0;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        pkg_send_ok <= 1'b0;
    else
        pkg_send_ok <= pkg_state == PKG_END;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        head_cnt <= 'd0;
    else if (pkg_state == PKG_SEND_NET_HEAD)
        head_cnt <= head_cnt + 1;
    else
        head_cnt <= 'd0;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        udp_cnt <= 'd0;
    else if (pkg_state == PKG_SEND_UDP)
        udp_cnt <= udp_cnt + 1;
    else
        udp_cnt <= 'd0;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        video_data_byte_cnt <= 'd0;
    else if (pkg_state == PKG_SEND_VIDEO_DATA)
        video_data_byte_cnt <= video_data_byte_cnt + 1;
    else
        video_data_byte_cnt <= 'd0;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_net_out_en <= 1'b0;
    else if (pkg_state == PKG_SEND_NET_HEAD || pkg_state == PKG_SEND_UDP || pkg_state == PKG_SEND_VIDEO_DATA)
        O_net_out_en <= 1'b1;
    else
        O_net_out_en <= 1'b0;

always @(posedge I_sclk)
    disp_data_ram_rddata_dly <= I_disp_data_ram_rddata;

always @(posedge I_sclk)
    if (udp_cnt == NET_UDP_BYTES - 1 - 5 - 1)
        begin
        row_and_col <= row[7:0];
        is_row_and_col <= 1'b1;
        end
    else if (udp_cnt == NET_UDP_BYTES - 1 - 4 - 1)
        begin
        row_and_col <= row[10:8];
        is_row_and_col <= 1'b1;
        end
    else if (udp_cnt == NET_UDP_BYTES - 1 - 3 - 1)
        begin
        row_and_col <= col[7:0];
        is_row_and_col <= 1'b1;
        end
    else if (udp_cnt == NET_UDP_BYTES - 1 - 2 - 1)
        begin
        row_and_col <= col[11:8];
        is_row_and_col <= 1'b1;
        end
    else
        is_row_and_col <= 1'b0;
   
always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_net_out_data <= 8'h0;
    else if (pkg_state == PKG_SEND_NET_HEAD)
        begin
        if (head_cnt == NET_HEAD_BYTES - 1'b1)
            O_net_out_data <= 8'hD5;
        else
            O_net_out_data <= 8'h55;
        end
    else if (pkg_state == PKG_SEND_UDP)
        begin
        if (is_row_and_col)
            O_net_out_data <= row_and_col;
        else
            O_net_out_data <= disp_data_ram_rddata_dly;
        end
    else if (pkg_state == PKG_SEND_VIDEO_DATA)
        begin
        if (is_r)
            O_net_out_data <= I_fifo_rddata[23:16];
        else if (is_g)
            O_net_out_data <= I_fifo_rddata[15:8];
        else if (is_b)
            O_net_out_data <= I_fifo_rddata[7:0];
        end
    else
        O_net_out_data <= 'd0;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        is_rgb_cnt <= 'd0;
    else if (pkg_state != PKG_SEND_VIDEO_DATA)
        is_rgb_cnt <= 'd0;
    else if (is_rgb_cnt >= 2)
        is_rgb_cnt <= 'd0;
    else
        is_rgb_cnt <= is_rgb_cnt + 1;

assign is_r = is_rgb_cnt == 'd0;
assign is_g = is_rgb_cnt == 'd1;
assign is_b = is_rgb_cnt == 'd2;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_fifo_rdreq <= 1'b0;
    else if (fifo_rdreq_cnt == cur_pkg_pixel_num)
        O_fifo_rdreq <= 1'b0;
    else if (pkg_state == PKG_SEND_UDP && udp_cnt == NET_UDP_BYTES - 2)
        O_fifo_rdreq <= 1'b1;
    else if (O_fifo_rdreq)
        O_fifo_rdreq <= 1'b0;
    else if (fifo_rdreq_dly)
        O_fifo_rdreq <= 1'b0;
    else if (pkg_state != PKG_SEND_VIDEO_DATA)
        O_fifo_rdreq <= 1'b0;
    else
        O_fifo_rdreq <= 1'b1;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        fifo_rdreq_dly <= 1'b0;
    else
        fifo_rdreq_dly <= O_fifo_rdreq;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        fifo_rdreq_cnt <= 'd0;
    else if (send_pkg_start)
        fifo_rdreq_cnt <= 'd0;
    else if (O_fifo_rdreq)
        fifo_rdreq_cnt <= fifo_rdreq_cnt + 1;

endmodule
`default_nettype wire

